﻿//-----------------------------------------------------------------------
// <copyright file="CycleTileData.cs" company="Steve Syrell">
//     Copyright 2012 Steve Syrell
// </copyright>
//-----------------------------------------------------------------------

namespace SteveSyrell.Tools.Phone.LiveTiles
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Runtime.Serialization;
    using System.Windows;
    using Shell = Microsoft.Phone.Shell;

    /// <summary>
    /// Cycle Tile Data.
    /// </summary>
    [DataContract]
    public class CycleTileData : TileData
    {
        /// <summary>
        /// The maximum number of cycle tile images.
        /// </summary>
        public const int MaxCycleTileImages = 9;

        /// <summary>
        /// The UI elements for this tile.
        /// </summary>
        private UIElement[] cycleUIs;

        /// <summary>
        /// The image URIs for this tile.
        /// </summary>
        private Uri[] cycleImages;

        /// <summary>
        /// Initializes a new instance of the CycleTileData class.
        /// </summary>
        public CycleTileData()
            : base()
        {
            this.SupportsWideTile = true;
        }

        /// <summary>
        /// Gets or sets the list of UI elements render to images and display on the cycle tile.
        /// <remarks>Any values in this array will be rendered to images and the resulting URIs will
        /// overwrite any values in the <see cref="CycleImages"/> array property.</remarks>
        /// </summary>
        [IgnoreDataMember]
        public UIElement[] CycleUIs
        {
            get
            {
                return this.cycleUIs;
            }

            set
            {
                if (value.Length > CycleTileData.MaxCycleTileImages)
                {
                    throw new InvalidOperationException("Cycle tiles cannot have more than" + CycleTileData.MaxCycleTileImages + " cycle images");
                }

                this.cycleUIs = value;
            }
        }

        /// <summary>
        /// Gets or sets the UI to use for the small size tile.
        /// </summary>
        [IgnoreDataMember]
        public UIElement SmallTileUI { get; set; }

        /// <summary>
        /// Gets or sets the list of image URIs to display on the cycle tile.
        /// <remarks>Any specified values in the <see cref="UIElemeents"/> array property will be rendered to images 
        /// and the resulting URIs will overwrite any values in this property</remarks>
        /// </summary>
        [DataMember]
        public Uri[] CycleImages
        {
            get
            {
                return this.cycleImages;
            }

            set
            {
                if (value.Length > CycleTileData.MaxCycleTileImages)
                {
                    throw new InvalidOperationException("Cycle tiles cannot have more than" + CycleTileData.MaxCycleTileImages + " cycle images");
                }

                this.cycleImages = value;
            }
        }

        /// <summary>
        /// Gets or sets the URI of the background image of the small size tile.
        /// </summary>
        [DataMember]
        public Uri SmallBackgroundImage { get; set; }

        /// <summary>
        /// Gets the system shell tile data that is used to actually create the tile in the system.
        /// </summary>
        /// <param name="verifyUiElements">A value indicating whether to verify UI elements.</param>
        /// <returns>The system shell tile data.</returns>
        internal override Shell.ShellTileData GetSystemShellTileData(bool verifyUiElements)
        {
            if (verifyUiElements)
            {
                // Make sure we have valid tile data.
                if (this.SmallTileUI == null)
                {
                    throw new InvalidOperationException("Small tile UI must be provided");
                }

                if (this.CycleUIs.Length == 0)
                {
                    throw new InvalidOperationException("At least one cycle tile UI must be provided");
                }
            }

            // Create all the background images.
            this.CreateAndSaveTileImages();

            // Now create the system tile data to return.
            Shell.CycleTileData tileDataToReturn = new Shell.CycleTileData
            {               
                Count = this.Count,
                SmallBackgroundImage = this.SmallBackgroundImage,
                Title = this.Title,
                CycleImages = this.CycleImages
            };

            return tileDataToReturn;
        }

        /// <summary>
        /// Cleans up all images created for this tile.
        /// </summary>
        internal override void CleanupResources()
        {
            // Clean up all cycle images.
            foreach (Uri image in this.CycleImages)
            {
                this.DeleteTileImage(image);
            }

            // Clean up the small background image.
            this.DeleteTileImage(this.SmallBackgroundImage);
        }

        /// <summary>
        /// Copies non-updated old tile data to this tile data instance.
        /// This is used when updating tile data if only new (updated) data is applied.
        /// </summary>
        /// <param name="oldTileData">The old tile data instance.</param>
        internal override void CopyNonUpdatedData(TileData oldTileData)
        {
            // First, cast the tile data to this specific tile data type.
            CycleTileData oldCycleTileData = oldTileData as CycleTileData;

            if (oldCycleTileData == null)
            {
                throw new InvalidOperationException("Updating to a different tile type is not supported.");
            }

            // For each of the tile images URIs, if this new instance's URI is null, it was not provided
            // and is not being updated. Therefore, use the existing (old) image.
            // For any images that we copy over to the new data, set it to null in the old data so that 
            // when we clean the old images up, we don't delete images that we're still using.
            if (this.SmallBackgroundImage == null)
            {
                this.SmallBackgroundImage = oldCycleTileData.SmallBackgroundImage;
                oldCycleTileData.SmallBackgroundImage = null;
            }

            for (int i = 0; i < this.CycleImages.Length; i++)
            {
                if (this.CycleImages[i] == null)
                {
                    this.CycleImages[i] = oldCycleTileData.CycleImages[i];
                    oldCycleTileData.CycleImages[i] = null;
                }
            }
        }

        /// <summary>
        /// Gets a list of the file names of the images in use for this tile.
        /// </summary>
        /// <returns>A list of the file names of the images in use for this tile.</returns>
        internal override List<string> GetImageFileNames()
        {
            List<string> imageFileNames = new List<string>();

            // Get the file names of each cycle image.
            foreach (Uri image in this.CycleImages)
            {
                imageFileNames.Add(Path.GetFileName(image.ToString()));
            }

            // Get the file name of the small background image.
            imageFileNames.Add(Path.GetFileName(this.SmallBackgroundImage.ToString()));

            return imageFileNames;
        }

        /// <summary>
        /// Creates and saves all images for this tile.
        /// </summary>
        protected override void CreateAndSaveTileImages()
        {
            int elementCount = this.CycleUIs.Length;

            // Create the images array if needed
            if (this.CycleImages == null)
            {
                this.CycleImages = new Uri[elementCount];
            }

            // Create the cycle images.
            for (int i = 0; i < elementCount; i++)
            {
                this.CycleImages[i] = this.CreateAndSaveTileImage(this.CycleUIs[i], TileData.WideTileSize);
                this.CycleUIs[i] = null;
                GC.Collect();
            }

            // Create the small background image.
            this.SmallBackgroundImage = this.CreateAndSaveTileImage(this.SmallTileUI, TileData.SmallTileSize);
            this.SmallTileUI = null;
            GC.Collect();
        }
    }
}
